#include "Shader.h"
#include <iostream>
#include <fstream>
#include <sstream>

void Shader::initShader(std::string vertextShaderPath, std::string fragmentShaderPath) {
	if (vertextShaderPath.empty() || fragmentShaderPath.empty()) {
		std::cout << "initShader()" << ", vertextPath is empty or fragmentShaderPath is empty, vertextShaderPath="
			<< vertextShaderPath << ", fragmentShaderPath=" << fragmentShaderPath << std::endl;
		return;
	}

	std::ifstream inVertextShaderFile(vertextShaderPath.c_str());
	std::ifstream inFragmentShaderFile(fragmentShaderPath.c_str());

	std::stringstream vertextBuffer;
	vertextBuffer << inVertextShaderFile.rdbuf();
	inVertextShaderFile.close();
	std::string vertexCode = vertextBuffer.str();
	const char* pVertextCode = vertexCode.c_str();

	mVertextShaderId = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(mVertextShaderId, 1, &pVertextCode, NULL);
	glCompileShader(mVertextShaderId);
	GLint vertexCompileStatus;
	glGetShaderiv(mVertextShaderId, GL_COMPILE_STATUS, &vertexCompileStatus);
	if (vertexCompileStatus != GL_TRUE) {
		GLint bufferLength;
		glGetShaderiv(mVertextShaderId, GL_INFO_LOG_LENGTH, &bufferLength);
		GLchar* pLogBuffer = new GLchar[bufferLength];
		GLint logLength;
		glGetShaderInfoLog(mVertextShaderId, bufferLength, &logLength, pLogBuffer);
		if (logLength > 0) {
			std::cout << "Compile vertex shader failed, pLogBuffer=" << pLogBuffer << std::endl;
		}
	}

	std::stringstream fragmentBuffer;
	fragmentBuffer << inFragmentShaderFile.rdbuf();
	inFragmentShaderFile.close();
	std::string fragmentCode = fragmentBuffer.str();
	const char* pFragmentCode = fragmentCode.c_str();

	mFragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(mFragmentShaderId, 1, &pFragmentCode, NULL);
	glCompileShader(mFragmentShaderId);
	GLint fragmentCompileStatus;
	glGetShaderiv(mFragmentShaderId, GL_COMPILE_STATUS, &fragmentCompileStatus);
	if (fragmentCompileStatus != GL_TRUE) {
		GLint bufferSize;
		glGetShaderiv(mFragmentShaderId, GL_INFO_LOG_LENGTH, &bufferSize);
		GLchar* pLogBuffer = new GLchar[bufferSize];
		GLint logLength;
		glGetShaderInfoLog(mFragmentShaderId, bufferSize, &logLength, pLogBuffer);
		if (logLength > 0) {
			std::cout << "Compile vertex shader failed, pLogBuffer=" << pLogBuffer << std::endl;
		}
		delete[] pLogBuffer;
	}

	mProgramId = glCreateProgram();
	glAttachShader(mProgramId, mVertextShaderId);
	glAttachShader(mProgramId, mFragmentShaderId);
	glLinkProgram(mProgramId);
	GLint programLinkStatus;
	glGetProgramiv(mProgramId, GL_LINK_STATUS, &programLinkStatus);
	if (programLinkStatus != GL_TRUE) {
		GLint bufferSize;
		glGetProgramiv(mProgramId, GL_INFO_LOG_LENGTH, &bufferSize);
		GLchar* pLogBuffer = new GLchar[bufferSize];
		GLint logLength;
		glGetProgramInfoLog(mProgramId, bufferSize, &logLength, pLogBuffer);
		delete[] pLogBuffer;
	}
	glDeleteShader(mVertextShaderId);
	glDeleteShader(mFragmentShaderId);
}

GLuint Shader::getProgramId() const {
	return mProgramId;
}

void Shader::useProgram() {
	glUseProgram(mProgramId);
}

void Shader::setInt(std::string name, GLint value) {
	GLuint location = glGetUniformLocation(mProgramId, name.c_str());
	glUniform1i(location, value);
}

void Shader::setFloat(std::string name, GLfloat value) {
	GLuint location = glGetUniformLocation(mProgramId, name.c_str());
	glUniform1f(location, value);
}

void Shader::setMat4(std::string name, GLfloat* pValue) {
	GLuint location = glGetUniformLocation(mProgramId, name.c_str());
	glUniformMatrix4fv(location, 1, GL_FALSE, pValue);

}

